package dao;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

//通过这个类封装针对数据库中file_meta表的操作
public class FileDao {
    //1.初始化数据库(建表)
    public void initDB() {
        //1)先能够读取到db.sql中的sql语句
        //2)根据sql语句调用jdbc执行操作
        Connection connection = null;
        Statement statement = null;
        try {
            connection = DBUtil.getConnection();
            statement = connection.createStatement();
            String[] sqls = getInitSQL();
            for (String sql : sqls) {
                System.out.println("[initDB] sql:"+ sql);
                statement.executeUpdate(sql);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection, statement, null);
        }
    }

    //从db.sql中读取文件内容
    //虽然当前db.sql中只有一个sql语句,但是原则上写了多个也要支持
    //所以返回值设定成了字符串数组,每个元素都是一个独立的sql语句.
    private String[] getInitSQL() {
        //存储最终结果
        StringBuilder stringBuilder = new StringBuilder();
        //此处需要动态获取到db.sql文件的路径,而不是一个写死的绝对路径
        try (InputStream inputStream = FileDao.class.getClassLoader().getResourceAsStream("db.sql")) {
            try (InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf8")) {
                while (true) {
                    //此处的ch的返回值是int,实际上是char,
                    //只不过为了表示-1的结束标记,使用了int
                    int ch = inputStreamReader.read();
                    if (ch == -1) {
                        //文件读取完毕
                        break;
                    }
                    stringBuilder.append((char) ch);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return stringBuilder.toString().split(";");
    }

    //2.插入文件/目录数据到数据库中
    //提供一个批量插入的方法
    public void add(List<FileMeta> fileMetas) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            connection = DBUtil.getConnection();
            //关闭自动提交功能
            connection.setAutoCommit(false);
            //构造批量的sql语句
            String sql = "insert into file_meta values(null,?,?,?,?,?,?,?)";
            preparedStatement = connection.prepareStatement(sql);
            for (FileMeta fileMeta : fileMetas) {
                //针对当前FileMeta对象,替换到sql语句中,把?替换成具体的数值
                preparedStatement.setString(1, fileMeta.getName());
                 preparedStatement.setString(2, fileMeta.getPath());
                preparedStatement.setBoolean(3, fileMeta.isDirectory());
                preparedStatement.setString(4, fileMeta.getPinyin());
                preparedStatement.setString(5, fileMeta.getPinyinFirst());
                preparedStatement.setLong(6, fileMeta.getSize());
                preparedStatement.setTimestamp(7, new Timestamp(fileMeta.getLastModified()));
                //使用addBatch,把这个构造好的片段,累积起来
                //addBatch会把已经构造好的sql保存好,同时又会允许重新构造一个新的sql出来
                preparedStatement.addBatch();
                System.out.println("[insert] 插入文件: "+ fileMeta.getPath()+File.separator+fileMeta.getName());
            }
            //执行所有的sql片段
            preparedStatement.executeBatch();
            //执行完毕之后,通过commit告知数据库,添加完毕了.
            connection.commit();
        } catch (SQLException e) {
            //上述代码出现异常,就进行回滚操作
            try {
                if (connection != null)
                    connection.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        } finally {
            DBUtil.close(connection,preparedStatement,null);
        }
    }

    //3.按照特定的关键词进行查询
    //这个实在实现文件搜索功能的时候,必备的部分
    //此处查询的pattern可能是文件名一部分,也可能是文件名拼音的一部分,还可能是拼音首字母的一部分
    public List<FileMeta> searchByPattern(String pattern) {
        List<FileMeta> fileMetas = new ArrayList<>();
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            connection = DBUtil.getConnection();
            String sql = "select name,path,is_directory,size,last_modified from file_meta"
                    + " where name like ? or pinyin like ? or pinyin_first like ?"
                    + " order by path,name";
            statement = connection.prepareStatement(sql);
            statement.setString(1,"%" + pattern + "%");
            statement.setString(2,"%" + pattern + "%");
            statement.setString(3,"%" + pattern + "%");
            resultSet = statement.executeQuery();
            while (resultSet.next()){
                String name = resultSet.getString("name");
                String path = resultSet.getString("path");
                boolean isDirectory = resultSet.getBoolean("is_directory");
                long size = resultSet.getLong("size");
                Timestamp lastModified = resultSet.getTimestamp("last_modified");
                FileMeta fileMeta = new FileMeta(name,path,isDirectory,size,lastModified.getTime());
                fileMetas.add(fileMeta);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return fileMetas;
    }


    //4.给定路径,查询这个路径对应的结果(此路径下都有哪些文件)
    //这个方法会在后续重新扫描,更新数据库的时候用到
    public List<FileMeta> searchByPath(String targetPath) {
        List<FileMeta> fileMetas = new ArrayList<>();
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            connection = DBUtil.getConnection();
            String sql = "select name,path,is_directory,size,last_modified from file_meta"
                    +" where path = ?";
            statement = connection.prepareStatement(sql);
            statement.setString(1,targetPath);
            resultSet = statement.executeQuery();
            while(resultSet.next()){
                String name = resultSet.getString("name");
                String path = resultSet.getString("path");
                boolean isDirectory = resultSet.getBoolean("is_directory");
                long size = resultSet.getLong("size");
                Timestamp lastModified = resultSet.getTimestamp("last_modified");
                FileMeta fileMeta = new FileMeta(name,path,isDirectory,size,lastModified.getTime());
                fileMetas.add(fileMeta);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return fileMetas;
    }

    //5.删除数据
    //这个文件已经从磁盘上删除了,此时就需要把表的内容也进行更新
    //进行删除的时候,可能当前被删除的是一个普通文件(直接删除表记录即可)
    //也有可能删除的时候,被删除的是一个目录,此时,就需要把目录里包含的子文件/子目录也一并删除掉
    public void delete(List<FileMeta> fileMetas) {
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            connection = DBUtil.getConnection();
            connection.setAutoCommit(false);
            //此处构造的sql要根据当前删除的内容情况,来区分对待
            for (FileMeta fileMeta:fileMetas) {
                String sql = null;
                if (!fileMeta.isDirectory()){
                    //针对普通文件
                    sql = "delete from file_meta where name=? and path=?";
                } else {
                    //针对目录的删除操作
                    //path like ? 要被替换成形如'd:/test%',删除子目录和子文件
                    sql = "delete from file_meta where (name=? and path=?) or (path like ?)";
                }
                //此处不能像前面的add一样使用addBatch,addBatch前提是,sql是一个模板
                //把?替换成不同的值,此处的sql不一定使相同的
                //所以此处就需要重新构造出statement对象表示这个sql了.
                statement = connection.prepareStatement(sql);
                if (!fileMeta.isDirectory()){
                    //普通文件,需要替换两个?
                    statement.setString(1,fileMeta.getName());
                    statement.setString(2,fileMeta.getPath());
                }else {
                    //针对目录
                    statement.setString(1,fileMeta.getName());
                    statement.setString(2,fileMeta.getPath());
                    statement.setString(3,fileMeta.getPath()+File.separator+fileMeta.getName()+File.separator+"%");
                }
                //真正执行删除操作
                statement.executeUpdate(sql);
                System.out.println("[delete]" + fileMeta.getPath() +'/'+ fileMeta.getName());
                statement.close();
            }
            connection.commit();
        } catch (SQLException e) {
                try {
                    connection.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
        }finally {
            DBUtil.close(connection,null,null);
        }
    }
}
